package com.imooc.reader.management.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.reader.management.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class SecurityConfiguration {

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint(){
        return (request,response,exception)->{
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(401);
            // 写出去
            Map<String, Object> map = new LinkedHashMap<>(4);
            map.put("code", "UNAUTHORIZED_ACCESS_IS_DENIED");
            map.put("message", "未经认证拒绝访问");
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(map);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return ((request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("code", "0");
            map.put("message", "success");
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(map);
            PrintWriter writer = response.getWriter();
            writer.write(json);
            writer.flush();
            writer.close();
        });
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler(){
        return ((request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("code", "0");
            map.put("message", "success");
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(map);
            PrintWriter writer = response.getWriter();
            writer.write(json);
            writer.flush();
            writer.close();
        });
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return ((request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(401);
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("code", exception.getMessage());
            String message = null;
            switch (exception.getMessage()){
                case "INCORRECT_USERNAME_OR_PASSWORD" -> message = "用户名或密码错误";
                case "THE_USER_HAS_BEEN_FROZEN" -> message = "该用户已被冻结";
            }
            map.put("message", message);
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(map);
            PrintWriter writer = response.getWriter();
            writer.write(json);
            writer.flush();
            writer.close();
        });
    }

    @Bean
    public AuthenticationProvider authenticationProvider(UserService userService){
        return new AuthenticationProvider() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                String username = authentication.getName();//用户名
                String password = authentication.getCredentials().toString();//密码
                UserDetails userDetails = userService.loadUserByUsername(username);
                PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
                if(passwordEncoder.matches(password,userDetails.getPassword())){
                    if(!userDetails.isEnabled()){
                        throw new DisabledException("THE_USER_HAS_BEEN_FROZEN");
                    }else{
                        return new UsernamePasswordAuthenticationToken(username, passwordEncoder, userDetails.getAuthorities());
                    }
                }else{
                    throw new BadCredentialsException("INCORRECT_USERNAME_OR_PASSWORD");
                }
            }

            @Override
            public boolean supports(Class<?> authentication) {
                return authentication.equals(UsernamePasswordAuthenticationToken.class);
            }
        };
    }
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.sessionManagement(sessionManagementCustomizer -> sessionManagementCustomizer.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED));
        http.headers(headersConfigurer -> headersConfigurer.frameOptions(frameOptionsConfig -> frameOptionsConfig.disable()));
        http.exceptionHandling(exceptionHandlingConfigurer -> exceptionHandlingConfigurer.authenticationEntryPoint(authenticationEntryPoint()));
        //标注的资源不需要验证，其他都需要
        http.authorizeHttpRequests(authz -> authz.requestMatchers("/assets/**", "/**.ico", "/images/**").permitAll()
                .anyRequest().authenticated()
        );
        http.formLogin(formLoginConfigurer -> formLoginConfigurer.loginProcessingUrl("/authentication")
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler())
                .loginPage("/login.html")
                .permitAll()
                /**
                 * POST /authentication
                 *
                 * username=abc&password=123
                 */
                .usernameParameter("username")
                .passwordParameter("password")
        );
        http.logout(logoutConfigurer -> logoutConfigurer.logoutUrl("/logout")
                .logoutSuccessHandler(logoutSuccessHandler())
                .invalidateHttpSession(true)
                .clearAuthentication(true));
        http.csrf(httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.disable());
        return http.build();
    }
}
